package com.hanxiaozhang.smallfunction.customannotation;

import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;

/**
 * 〈一句话功能简述〉<br>
 * 〈〉
 *
 * @author hanxinghua
 * @create 2022/11/4
 * @since 1.0.0
 */
public class Test {

    public static void main(String[] args) throws Exception {

        Bean bean = new Bean(1L, "xiaozhang", 17, null);
        System.out.println(bean.getSex());

        Map<String, String> map = beanToMap(bean);
        map.forEach((k, v) -> {
            System.out.println("k: " + k + " ,v: " + v);
        });

        map.put("T_NO", "2");
        map.put("T_ADDRESS", "北京");

        Bean newBean = mapToBean(map, new Bean());

        System.out.println(newBean);

    }

    /**
     * map转换成bean
     * <p>
     * 支持同一个map的kv映射到多个属性上
     *
     * @param map
     * @param t
     * @param <T>
     * @return
     * @throws Exception
     */
    public static <T> T mapToBean(Map<String, String> map, T t) throws Exception {
        if (t == null || map == null || map.isEmpty()) {
            return t;
        }
        Class<?> beanClass = t.getClass();
        Field[] fields = beanClass.getDeclaredFields();
        if (fields.length == 0) {
            return t;
        }
        Map<String, List<String>> keyNameAttrMap = new HashMap<>(16);
        String mapKeyName;
        for (Field field : fields) {
            BeanToMap fieldAnnotation = field.getDeclaredAnnotation(BeanToMap.class);
            if (fieldAnnotation != null) {
                mapKeyName = fieldAnnotation.mapKeyName();
                if (keyNameAttrMap.containsKey(mapKeyName)) {
                    keyNameAttrMap.get(mapKeyName).add(field.getName());
                } else {
                    List<String> list = new ArrayList<>();
                    list.add(field.getName());
                    keyNameAttrMap.put(fieldAnnotation.mapKeyName(), list);
                }
            }
        }
        for (Map.Entry<String, String> entry : map.entrySet()) {
            String keyName = entry.getKey();
            if (keyNameAttrMap.containsKey(keyName)) {
                List<String> list = keyNameAttrMap.get(keyName);
                for (String name : list) {
                    Field field = beanClass.getDeclaredField(name);
                    if (Modifier.isFinal(field.getModifiers())) {
                        continue;
                    }
                    setValue(t, entry.getValue(), field);
                }
            }
        }
        return t;
    }

    private static <T> void setValue(T t, String value, Field field) throws IllegalAccessException {
        if (StringUtils.isBlank(value)) {
            return;
        }
        field.setAccessible(true);
        Class<?> type = field.getType();
        if (type == Integer.class) {
            field.set(t, Integer.parseInt(value));
        } else if (type == Long.class) {
            field.set(t, Long.parseLong(value));
        } else if (type == Double.class) {
            field.set(t, Double.parseDouble(value));
        } else if (type == Boolean.class) {
            field.set(t, Boolean.parseBoolean(value));
        } else if (type == String.class) {
            field.set(t, value);
        } else {
            throw new RuntimeException("Not support type");
        }
    }

    /**
     * bean转换成map
     *
     * @param t
     * @param <T>
     * @return
     * @throws Exception
     */
    private static <T> Map<String, String> beanToMap(T t) throws Exception {
        Map<String, String> map = new HashMap<>(8);
        Class<?> beanClass = t.getClass();
        Field[] fields = beanClass.getDeclaredFields();
        for (Field field : fields) {
            BeanToMap fieldAnnotation = field.getDeclaredAnnotation(BeanToMap.class);
            if (fieldAnnotation != null) {
                field.setAccessible(true);
                Object value = field.get(t);
                if (fieldAnnotation.notNull() && value == null) {
                    throw new NullPointerException("[ "+field.getName() + " ] value is null");
                }
                if (value == null) {
                    continue;
                }
                Class<?> type = field.getType();
                Boolean supportFlag = type == Integer.class || type == Long.class ||
                        type == Double.class || type == Boolean.class || type == String.class;
                if (!supportFlag) {
                    throw new RuntimeException("Not support type");
                }
                map.put(fieldAnnotation.mapKeyName(), String.valueOf(value));
            }
        }
        return map;
    }


}
